﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using TCCUIControls;

namespace TCCHWLib
{
    public class Memory
    {
        private SortedDictionary<int, MemoryEntry> m_Buffer = new SortedDictionary<int, MemoryEntry>();
        private Size m_Capacity;
        private SortedDictionary<int, int> m_FreeAddress = new SortedDictionary<int, int>();
        private int m_EmptyAddressCount;
        protected UISharedMemory m_UiSharedMemory = null;
        private CommandListView m_ListView = null;

        public object this[int index]
        {
            get { return Read(index); }
        }

        public CommandListView ListView
        {
            get { return m_ListView; }
            set { m_ListView = value; }
        }

        public UISharedMemory UISharedMemory
        {
            get { return m_UiSharedMemory; }
            set
            {
                m_UiSharedMemory = value;
            }
        }

        public Size Capacity
        {
            get { return m_Capacity; }
            set { m_Capacity = value; }
        }
        public SortedDictionary<int, MemoryEntry> Buffer
        {
            get { return m_Buffer; }
            set { m_Buffer = value; }
        }
        public Memory(Size capacity)
        {
            m_Capacity = capacity;
            m_EmptyAddressCount = capacity.Height;
            m_Buffer = new SortedDictionary<int, MemoryEntry>();
            m_ListView = new CommandListView();
            m_UiSharedMemory = new UISharedMemory("Shared Memory", m_ListView);
       }
        public virtual int Allocate(int size)
        {
            lock (m_Buffer)
            {
                int newAddress;
                if (m_EmptyAddressCount < size) // checking over flow 
                {
                    throw new Exception("Cache over flow");
                }
                if (FreeAddressContainSpace(size)) //free adddress list contain same size
                {
                    newAddress = GetFreeAddress(size);
                }
                else
                {

                    newAddress = m_Buffer.Count; // gets the last address from buffer
                    if ((newAddress + size) >= m_Capacity.Height) // make sure that no buffer flow
                    {
                        throw new Exception("Cache over flow");
                    }
                }
                MemoryEntry newentry = new MemoryEntry(null); // create empty memory entry
                newentry.Valid = true;
                for (int i = 0; i < size; i++)
                {
                    newentry = new MemoryEntry(null);
                    newentry.Valid = true;
                    if (m_Buffer.ContainsKey(newAddress + i))
                        if (m_Buffer[newAddress + i].Valid == false)
                            m_Buffer[newAddress + i].Valid = true;
                        else
                            m_Buffer.Add((newAddress + i), newentry);
                    else
                        m_Buffer.Add((newAddress + i), newentry);
                }
                UpdateListView();
                return newAddress;
            }
        }
        public virtual int Allocate()
        {
            return Allocate(1); //allocating one address
        }
        public virtual void Deallocate(int address, int size)
        {
            for (int i = 0; i < size; i++)
            {

                if (m_Buffer.ContainsKey(address))
                {
                    m_Buffer[address + i].Valid = false;
                    m_EmptyAddressCount++;
                }
                else
                    throw new Exception("deallocating address which is not found in shared memory");

            }
            if (!m_FreeAddress.ContainsKey(address))
                m_FreeAddress.Add(address, size);
            UpdateListView();
            return;
        }
        public virtual void Deallocate(int address)
        {
            if (m_Buffer.ContainsKey(address))
                Deallocate(address, 1);
            else
                throw new Exception("deallocating address which is not found in shared memory");

            
            return;
        }
        public virtual object Read(int address)
        {
            object value = null;
            if (m_Buffer.ContainsKey(address))
            {
                if (m_Buffer[address].Valid == true)
                    value = m_Buffer[address].Value;
                else
                    return null;

            }
            else
                throw new Exception("reading address which is not found in shared memory");

            UpdateListView();
            return value;
        }
        public virtual bool Write( int address,object value)
        {
            if (m_Buffer.ContainsKey(address))
            {
                if (m_Buffer[address].Valid == true)
                    m_Buffer[address].Value = value;
                else
                    return false;
            }
            else
                throw new Exception("writing address which is not found in shared memory");

            UpdateListView();
            return true;
        }
        delegate void UpdateListViewDelegate(IDictionary<int, MemoryEntry> buffer);
        delegate void UpdateListViewDelegate2(IDictionary<int, CacheEntry> buffer);
        public virtual void UpdateListView()
        {
            UpdateListView(m_Buffer);
        }
        public void UpdateListView(IDictionary<int, MemoryEntry> buffer)
        {
            if (m_ListView.InvokeRequired)
            {
                m_ListView.BeginInvoke(new UpdateListViewDelegate(UpdateListView), buffer);
                return;
            }

            lock (buffer)
            {
                m_ListView.Items.Clear();

                foreach (KeyValuePair<int, MemoryEntry> pair in buffer)
                {
                    m_ListView.Items.Add(pair.Value.ToString()); // Content
                    m_ListView.Items[m_ListView.Items.Count - 1].Tag = pair.Key; // Address
                }
            }
        }
        public void UpdateListView(IDictionary<int, CacheEntry> buffer)
        {
            if (m_ListView.InvokeRequired)
            {
                m_ListView.BeginInvoke(new UpdateListViewDelegate2(UpdateListView), buffer);
                return;
            }

            lock (buffer)
            {
                m_ListView.Items.Clear();

                foreach (KeyValuePair<int, CacheEntry> pair in buffer)
                {
                    m_ListView.Items.Add(pair.Value.ToString()); // Content
                    m_ListView.Items[m_ListView.Items.Count - 1].Tag = pair.Key; // Address
                }
                m_ListView.Invalidate();
            }
        }

        #region HelperFunctions
        /// <summary>
        /// Gets the free address from the free address list using first fit.
        /// </summary>
        /// <param name="size">the size of the allocated object</param>
        /// <returns></returns>
        int GetFreeAddress(int size)
        {
            foreach (int key in m_FreeAddress.Keys)
            {
                if (m_FreeAddress[key] >= size)
                {
                    m_FreeAddress.Remove(key);
                    return key;
                }
            }
            throw new Exception("Couldnt return the free key");
        }
        /// <summary>
        /// Making sure there is a space in the buffer to add new address
        /// </summary>
        /// <param name="size">size of the address</param>
        /// <returns></returns>
        bool FreeAddressContainSpace(int size)
        {
            foreach (int key in m_FreeAddress.Keys)
            {
                if (m_FreeAddress[key] >= size)
                {
                    return true;
                }
            }
            return false;
        }

        public override string ToString()
        {
            string memory = "";
            foreach (int i in m_Buffer.Keys)
            {
                memory += i.ToString();
                memory += "---";
                if (m_Buffer[i].Value != null)
                    memory += m_Buffer[i].Value.ToString();
                else
                    memory += "null";

                memory += "\n";
            }
            return memory;
        }
        #endregion
    }

}
